// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)

#ifndef MUDUO_BASE_MUTEX_H
#define MUDUO_BASE_MUTEX_H

#include <muduo/base/CurrentThread.h>
#include <boost/noncopyable.hpp>
#include <assert.h>
#include <pthread.h>

#ifdef CHECK_PTHREAD_RETURN_VALUE

#ifdef NDEBUG
__BEGIN_DECLS
extern void __assert_perror_fail(int errnum, // 错误码
	const char *file, // 文件
	unsigned int line, // 行数
	const char *function) // 当前的函数名称
__THROW __attribute__((__noreturn__));
__END_DECLS
#endif

// 让我来解析一下这个宏究竟干了一些什么东西。
// 首先定义了一个ret类型的变量errnum,然后值为ret
// __builtin_expect宏，就是预测errnum!=0的概率是比较小的，所以gcc会帮你做一些优化
// 抛弃这个宏的话，其实就是想表达if(errnum != 0) ..一般来说表示发生了错误
// 然后就是要做一些处理吧！
#define MCHECK(ret) ({ __typeof__ (ret) errnum = (ret);         \
                       if (__builtin_expect(errnum != 0, 0))    \
                         __assert_perror_fail (errnum, __FILE__, __LINE__, __func__);})

#else  // CHECK_PTHREAD_RETURN_VALUE

#define MCHECK(ret) ({ __typeof__ (ret) errnum = (ret);         \
                       assert(errnum == 0); (void) errnum;})

#endif // CHECK_PTHREAD_RETURN_VALUE

namespace muduo
{

// Use as data member of a class, eg.
//
// class Foo
// {
//  public:
//   int size() const;
//
//  private:
//   mutable MutexLock mutex_;
//   std::vector<int> data_; // GUARDED BY mutex_
// };
	class MutexLock : boost::noncopyable
	{
	public:
		MutexLock()
			: holder_(0)
		{
			MCHECK(pthread_mutex_init(&mutex_, NULL));
		}

		~MutexLock()
		{
			assert(holder_ == 0);
			MCHECK(pthread_mutex_destroy(&mutex_));
		}

		  // must be called when locked, i.e. for assertion
		bool isLockedByThisThread() const
		{
			return holder_ == CurrentThread::tid();
		}

		void assertLocked() const
		{
			assert(isLockedByThisThread());
		}

		  // internal usage

		void lock()
		{
			MCHECK(pthread_mutex_lock(&mutex_));
			assignHolder();
		}

		void unlock()
		{
			unassignHolder();
			MCHECK(pthread_mutex_unlock(&mutex_));
		}

		pthread_mutex_t* getPthreadMutex() /* non-const */
		{
			return &mutex_;
		}

	private:
		friend class Condition;

		class UnassignGuard : boost::noncopyable
		{
		public:
			UnassignGuard(MutexLock& owner)
				: owner_(owner)
			{
				owner_.unassignHolder();
			}

			~UnassignGuard()
			{
				owner_.assignHolder();
			}

		private:
			MutexLock& owner_;
		};

		void unassignHolder()
		{
			holder_ = 0;
		}

		void assignHolder()
		{
			holder_ = CurrentThread::tid();
		}

		pthread_mutex_t mutex_;
		pid_t holder_;
	};

	// Use as a stack variable, eg.
	// int Foo::size() const
	// {
	//   MutexLockGuard lock(mutex_);
	//   return data_.size();
	// }
	class MutexLockGuard : boost::noncopyable
	{
	public:
		explicit MutexLockGuard(MutexLock& mutex)
			: mutex_(mutex)
		{
			mutex_.lock();
		}

		~MutexLockGuard()
		{
			mutex_.unlock();
		}

	private:

		MutexLock& mutex_;
	};

}

// Prevent misuse like:
// MutexLockGuard(mutex_);
// A tempory object doesn't hold the lock for long!
#define MutexLockGuard(x) error "Missing guard object name"

#endif  // MUDUO_BASE_MUTEX_H
